/* Include files. */

extern "C" { 
	#include "standard.h"
	#include "util.h"
	#include "debug.h"
} 
#include "network.h"
#include "netwebcgi.h"
#include "subsystemcore.h"

/* Local function declarations. */

/* Macro API definitions. */

/* Global variable declarations. */

/* Type definitions. */

/* Local function definitions. */

#ifdef U_LOOP
netWebcgi::netWebcgi(u32 ip, u16 port, u32 fd)
		:netObject(ip, port,fd), m_ref(-1)
{
	_agent_name = string("access");	
	m_msg = json_object_new_object();
	if(NULL == m_msg) {
		return;
	}

	struct json_object *op = json_object_new_string("REQUEST");
	if(NULL == op) {
		return;
	}

	json_object_object_add(m_msg, "op", op);

	struct json_object *source = json_object_new_string("access/User.Webcgi");
	if(NULL == source) {
		return;
	}

	json_object_object_add(m_msg, "resource", source);
}

netWebcgi::~netWebcgi()
{
	FCGX_Finish_r(&m_request);
	if(NULL != m_msg)
		json_object_put(m_msg);

	if(m_ref > 0) {
		luaContext::getInstance().unref(m_ref);
	}
}

int netWebcgi::bind()
{
	int ret;

	if (subSystem::bindSubSystem(_agent_name, this)) {
		DEBUG("new connection " NIPQUAD_FMT ":%d , netobj = %p, socket = %d, id = %u\n",
		 NIPQUAD(_ip), _port, this, _fd, _id);
		ret = 1;
	}
	else {
		ERROR("%s service don't start!\n", _agent_name.c_str());
		ERROR("bind faild, " NIPQUAD_FMT ":%d , netobj = %p, socket = %d, id = %u\n",
		  NIPQUAD(_ip), _port, this, _fd, _id);
		ret = 0;
	}

	return ret;
}

void netWebcgi::accept()
{
	FCGX_Init();
	memset(&m_request, 0, sizeof(m_request));
	FCGX_InitRequest(&m_request, -1, 0);
	m_request.keepConnection = 1;
	m_request.ipcFd = _fd;

	lua_State *L = luaContext::getInstance().getState();
	m_L = L;

	lua_getglobal(L, "dealRequest");    // ��ȡlua����f
	if(NULL == L) {
		ERROR("Can't get lua state, exit from recvMsg\r\n");
	}

	lua_pushlightuserdata(L, this);
	m_state = FCGX_REQUEST_WATING;

	lua_pcall(L, 1, 1, 0);

	if(!lua_isnil(L, -1)) {
		m_ref = luaContext::getInstance().ref(1);
	}

	lua_pop(L, lua_gettop(L));
}

#define READ_BUFF (1024 * 4)



#ifdef U_LOOP

#define METANAME "fcgx"

static int
fcgx_lua_accept(lua_State *L)
{
	netWebcgi *pdata = (netWebcgi *)lua_touserdata(L, -1);
	if(NULL == pdata) {
		lua_pushinteger(L, -1);
		printf("accept failed");
		return 1;
	}

	int ret = pdata->fcgxAccept(L);
	return ret;
}

static int
fcgx_lua_readline(lua_State *L)
{
	netWebcgi *pdata = (netWebcgi *)lua_touserdata(L, -1);
	if(NULL == pdata) {
		lua_pushnil(L);
		return 1;
	}

	int ret = pdata->readLine(L);
	return ret;
}

static int
fcgx_lua_writeline(lua_State *L)
{
	netWebcgi *pdata = (netWebcgi *)lua_touserdata(L, -2);
	if(NULL == pdata) {
		lua_pushinteger(L, -1);
		ERROR("no pdata exit from fcgx_lua_writeline\r\n");
		return 1;
	}

	int ret = pdata->writeLine(L);
	return ret;
}

static int
fcgx_lua_envs(lua_State *L)
{
	netWebcgi *pdata = (netWebcgi *)lua_touserdata(L, -1);
	if(NULL == pdata) {
		lua_pushnil(L);
		ERROR("fcgx ctx is not right, exit from fcgx_lua_envs\r\n");
		return 1;
	}

	return pdata->dumpCGIEnves(L);
	return 1;
}

static int
fcgx_lua_sendSubSystem(lua_State *L)
{
	netWebcgi *pdata = (netWebcgi *)lua_touserdata(L, -2);
	if(NULL == pdata) {
		ERROR("fcgx ctx is not right, exit from fcgx_lua_sendSubSystem\r\n");
		return 0;
	}

	const char *msg = lua_tostring(L, -1);

	pdata->sendSubSystem(msg);

	return 0;
}

static const luaL_Reg fcgx[] = {
	{ "accept", fcgx_lua_accept},
	{ "readLine", fcgx_lua_readline },
	{ "writeLine", fcgx_lua_writeline },
//	{ "finish", fcgx_lua_finish },
	{ "envs", fcgx_lua_envs },
	{ "sendSubSystem", fcgx_lua_sendSubSystem },
	{ NULL, NULL },
};

void setEnvs(lua_State *L)
{
	lua_getglobal(L, "package");
	lua_pushstring(L, "path");
	lua_pushstring(L, "/usr/local/share/lua/5.3/mu/?.lua;/usr/local/share/lua/5.3/?.lua;/sbin/script/?.lua");
    lua_settable(L, -3);
	lua_pushstring(L, "cpath");
	lua_pushstring(L, "/usr/local/lib/lua/5.3/mu/?.so;/usr/local/lib/lua/5.3/?.so;");
    lua_settable(L, -3);
	lua_pop(L, 1);
}

luaContext::luaContext()
{
	int err;
	m_L = luaL_newstate();
	if(m_L) {
		luaL_openlibs(m_L);
		setEnvs(m_L);
		if((err = luaL_loadfile(m_L, "./script/fcgi.lua"))){  
			ERROR("%s\n", lua_tostring(m_L, -1));
		}
		ASSERT(err == LUA_OK);
		lua_pcall(m_L, 0, 0, 0);
		/* create module */
		luaL_register(m_L, "fcgx", fcgx);
	}
}

luaContext::~luaContext()
{
	if(NULL != m_L)
		lua_close(m_L);
}

lua_State* luaContext::getState()
{
	return m_L;
}

void luaContext::unref(int k)
{
	if(m_L) {
		lua_unref(m_L, k);
	}
}

int luaContext::ref(int k)
{
	if(m_L) {
		return lua_ref(m_L, k);
	}

	return -1;
}

luaContext& luaContext::getInstance()
{
	static luaContext ctx;
	return ctx;
}

int FCGX_GetSLine(char *str, int n, FCGX_Stream *stream)
{
	int j = 0;
	int c;
	int len = 0;

	for(; j < n; ++j) {
		c = FCGX_GetChar(stream);
		if(c == -1)
			break;

		str[len++] = c;
		if(c == '\n')
			break;
	}

	return len;
}

int netWebcgi::fcgxAccept(lua_State *L)
{
	m_L = L;
	FCGX_Finish_r(&m_request);

	if(m_state == FCGX_REQUEST_QUEUED) {
		int ret = FCGX_Accept_r(&m_request);
		m_state = FCGX_REQUEST_WATING;
		lua_pushinteger(L, ret);
		return 1;
	}

	m_state = FCGX_REQUEST_WATING;
	return lua_yield(L, lua_gettop(L));
}

int netWebcgi::readLine(lua_State *L)
{
	char buf[8192];
	int len = FCGX_GetSLine(buf, sizeof(buf), m_request.in);
	if(len > 0) {
		lua_pushlstring(L, buf, len);
		return 1;
	}

	if(len == 0) {
		lua_pushnil(L);
		return 1;
	}

	m_state = FCGX_REQUEST_READ_LSTR;
	return lua_yield(L, lua_gettop(L));
}

int netWebcgi::writeLine(lua_State *L)
{
	size_t len = 0;
	const char *msg = lua_tolstring(L, -1, &len);
	if(len && m_request.out) {
		FCGX_PutStr(msg, len, m_request.out);
	}

	lua_pushinteger(L, len);
	return 1;
}

int netWebcgi::sendSubSystem(const char *msg)
{
	struct json_object *data = json_object_new_string(msg);
	if(NULL == data)
		return e_CONTINUE;
	json_object_object_add(m_msg, "data", data);
	::sendSubSystem(_sub->_L, "httpd", _sub->_name.c_str(), "cgi", json_object_get_string(m_msg), _id);
	lua_yield(m_L, 0);
}

int netWebcgi::dumpCGIEnves(lua_State *L)
{
	lua_newtable(L);

	const char *name;
	const char *value;
	for(char **pos = m_request.envp; *pos; ++pos) {
		name = *pos;
		value = strchr(name, '=');
		if(!value)
			continue;		

		lua_pushlstring(L, name, value - name);
		lua_pushstring(L, value + 1);
		lua_rawset(L, -3);
	}

	return 1;
}

#endif

int netWebcgi::recvMsg()
{
#ifdef U_LOOP
	int ret;
	char buf[8193];
	char len;
	const char *tmp;
	int status;

	switch(m_state) {
	case FCGX_REQUEST_WATING:
		ret = FCGX_Accept_r(&m_request);
		lua_pushinteger(m_L, ret);
		status = lua_resume(m_L, NULL, 1);
		break;
	case FCGX_REQUEST_READ_STR:
		ret = FCGX_GetStr(buf, sizeof(buf) - 1, m_request.in);
		if(ret > 0)
			lua_pushlstring(m_L, buf, len);
		else
			lua_pushnil(m_L);
		status = lua_resume(m_L, NULL, 1);
		break;
	case FCGX_REQUEST_READ_LINE:
		tmp = FCGX_GetLine(buf, sizeof(buf) - 1, m_request.in);
		if(NULL != tmp)
			lua_pushstring(m_L, buf);
		else {
			lua_pushnil(m_L);
			ret = -1;
		}
		status = lua_resume(m_L, NULL, 1);
		break;
	case FCGX_REQUEST_READ_LSTR:
		len = FCGX_GetSLine(buf, sizeof(buf) - 1, m_request.in);
		if(len > 0) {
			lua_pushlstring(m_L, buf, len);
			return 1;
		}

		if(len == 0) {
			lua_pushnil(m_L);
			return 1;
		}
		status = lua_resume(m_L, NULL, 1);
		break;
	}

	if(m_request.ipcFd < 0)
		return e_ERROR;
	return e_OK;
#endif
}

int netWebcgi::sendMsg()
{
#ifdef U_LOOP
	string s = _out_queue.front();
	_out_queue.pop();
	lua_pushstring(m_L, s.c_str());
	lua_resume(m_L, NULL, 1);
	if(m_request.ipcFd < 0)
		return e_ERROR;
	return e_OK;
#endif
}
#else
netWebcgi::netWebcgi(u32 ip, u16 port, u32 fd)
		:netObject(ip, port,fd) 
{
	_agent_name = string("access");	
}

netWebcgi::~netWebcgi() 
{

}

int netWebcgi::bind()
{
	return 1;
}

int netWebcgi::recvMsg()
{
	return e_OK;
}

int netWebcgi::sendMsg()
{
	return e_OK;
}
#endif

